Спринт 2/18 → Тема 6/6: Основы отладки программ → Урок 2/3
Охотник за багами: The Python Debugger
В Python «из коробки» встроена интерактивная среда отладки программ — The Python Debugger (pdb). Этот инструмент позволяет приостановить работу программы в указанном месте и продолжить выполнение кода построчно, используя терминал. Так разработчик может проверить работоспособность каждой строчки кода и найти те места в программе, которые приводят к неожиданному результату.
У отладчика pdb весьма обширный функционал, подробно он описан в документации. Сейчас вам не понадобятся все его возможности, достаточно будет разобраться в том, как выполнить программу пошагово, чтобы найти причины ошибок.
Начинаем дебажить
Откройте в редакторе кода проект calc_and_win и активируйте виртуальное окружение, если ещё не сделали этого.
Запустите файл main.py. Для этого в терминале выполните команду:
BASH
Отладчик запустит файл и остановит выполнение кода на первой строке; в терминале появится приглашение для ввода команд отладчика — это строка, которая начинается с идентификатора
(pdb):В режиме отладки (или дебага) используются специальные команды для управления ходом выполнения программы. Вот описание основных команд:
h (help)— вывести все доступные команды pdb.l (list)— вывести в терминал по пять строк до и после строки, на которую интерпретатор указывает в данный момент.s (step)— выполнить строку, на которую указывает интерпретатор в данный момент, и остановить выполнение программы на следующей строке, если это возможно.n (next)— эта команда очень похожа на командуstep, но она будет выполнять программу не строго по шагам. Командаnextне станет заходить внутрь функций, а выполнит их целиком, в один шаг.b (break) имя_файла:номер_строки— указать место обязательной остановки выполнения программы. Место обязательной остановки программы разработчики называют «точка останова» или «брейкпоинт».c (continue)— выполнять программу до тех пор, пока она не отработает полностью или пока её выполнение не будет прервано ошибкой. Также выполнение этой команды будет прервано достижением точки останова — брейкпоинта.p имя_переменной— вывести текущее значение переменной в терминал.q (quit)— остановить работу отладчика.
В запущенном отладчике введите команду
c. Программа начнёт выполняться и остановится, как только упрётся в ошибку:Подобный трейсбек вы уже видели в прошлом уроке, сейчас ваша задача — найти и устранить причину его появления.
Выведите в терминал участок кода с ошибкой, для этого выполните команду
l:Символ
–> указывает на строку, на которой программа перестала выполняться. Эта строка отвечает за вывод сообщения о том, что в игре нет введённой команды. Но как так? Ведь мы ввели именно то, что просила программа!Причина ошибки в том, что программа требует не тот набор значений, который задан в ключах словаря.
Чтобы избавиться от ошибки, нужно расширить словарь
yes_no. Остановите работу отладчика, поправьте и сохраните код в файле module.py:PYTHON
Снова запустите программу в режиме отладчика и дайте команду выполнять программу до тех пор, пока она не отработает полностью или пока её выполнение не будет прервано ошибкой — введите команду
c. Выполните пять атак противника в режиме отладчика и введите команду 'y', чтобы сыграть в игру ещё раз, или команду 'n', чтобы закончить игру и вернуться к отладке программы. Всё должно отработать без проблем.Одна ошибка в программе исправлена, но есть ещё одна — в ходе тестирования игры было выявлено, что она неправильно считает количество очков урона, нанесённое противнику. Это логическая ошибка, интерпретатор не умеет находить такие ошибки. Значит, всё в ваших руках.
Ищем логическую ошибку
Чтобы найти логическую ошибку в чужом коде, можно пройти программу полностью, шаг за шагом. Но такой подход может занять много времени. Более подходящее решение — проанализировать код и результаты запуска программы перед началом отладки.
В файле main.py нет никаких расчётов, но к этому файлу подключена функция
run_game() из модуля module.py. Можно сделать вывод, что искать неправильный расчёт нужно именно в функции run_game(). Функция run_game() вызывается внутри функции main() в файле main.py. Значит, первый шаг в процессе поиска истины — дойти в режиме дебаггинга до функции main() в файле main.py.Запустите программу в режиме дебаггинга и воспользуйтесь командой
n, чтобы не идти по шагам через все этапы импорта, до тех пор пока не дойдёте до строки с вызовом функции main(). Ваш путь должен быть таким:BASH
Если бы вы видели этот код впервые, то вам было бы неизвестно, как работает подключённый модуль, поэтому далее лучше идти по шагам, чтобы ничего не упустить. Ваша задача — найти функцию, в которой неправильно считаются очки атаки.
Используя команду
s, дойдите до того момента, когда программа предложит вам ввести тип атаки.Вы дошли до места в программе, где пользователь начинает вводить тип атак. Значит, момент, где очки всех атак будут суммироваться, где-то рядом. Продолжайте дебажить программу по шагам; функции стандартной библиотеки можно выполнять целиком с помощью команды
n. Ваш путь должен получиться таким:BASH
Итак, пользователь выполнил атаку, и в переменную
attack_value записалось случайное значение. Далее это случайное значение должно прибавиться к переменной total, в которой суммируются все очки атак. Но мы видим другую картину: вместо значения, полученного в attack_value, к переменной total добавляется единица.Ошибка обнаружена! Но не будем спешить с её обезвреживанием. Мы прошли ещё не по всем путям её поиска.
Брейкпоинты: короткий путь к ошибке
Можно не проходить всю программу по шагам. Для этого существуют брейкпоинты — места обязательной остановки программы. До брейкпоинта программа будет выполняться в обычном режиме, что поможет сэкономить время.
Брейкпоинты лучше всего устанавливать как можно ближе к месту возможной ошибки. Вы уже знаете, что в тестируемой вами игре ошибка возникает в функции
get_user_attack() — значит, лучшим выбором места для брейкпоинта будет строка, где происходит вызов этой функции.Остановите отладчик, запустите его снова и установите брейкпоинт на строке, в которой происходит вызов функции
get_user_attack():BASH
Введите команду
c: до брейкпоинта программа выполнится в обычном режиме.BASH
Вот она — «проблемная» функция. Далее лучше идти по шагам, чтобы ничего не упустить — и вы придёте к ошибке, которую уже обнаружили ранее.
Пора бы её уже и исправить, но потерпите ещё немного. Есть ещё один способ добраться до ошибок в коде — используя интерфейс редактора кода, например VSCode.